package icasue.base.utils.wareable;

import lombok.extern.slf4j.Slf4j;
import org.springframework.cglib.beans.BeanCopier;
import org.springframework.cglib.core.Converter;
import org.springframework.util.Assert;

import java.lang.reflect.Constructor;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Consumer;
import java.util.stream.Collectors;

/**
 * @Author: Qiao Hang
 * @CreateDate: 2020/5/28 下午2:17
 * @UpdateDate:
 * @Description:
 */
@Slf4j
public class BeanCopierUtil {

    //copier 容器，存储Source 和 Target类信息的BeanCopier实例.
    public static volatile ConcurrentHashMap<String, BeanCopier> copierBucket = new ConcurrentHashMap<>();

    /**
     * 简单Copy属性.
     * @param source
     * @param targetClass
     * @param <T>
     * @param <S>
     * @return
     */
    public static  <T,S> T simpleCopier(S source,Class<T> targetClass){
        //尝试从容器中获取支持Source和Target 的 Copier实例
        BeanCopier supportedCopier = BeanCopierUtil.getSupportedCopier(source.getClass(), targetClass);
        //尝试copy.
        T target = BeanCopierUtil.copy(source, targetClass, supportedCopier);
        return target;
    }

    /**
     * Copy属性 到给出的实例.
     * @param source
     * @param target
     * @param <T>
     * @param <S>
     * @return
     */
    public static  <T,S> T simpleCopierDirectly(S source,T target){
        Assert.notNull(target,"simpleCopierDirectly required target must not be null.");
        //尝试从容器中获取支持Source和Target 的 Copier实例
        BeanCopier supportedCopier = BeanCopierUtil.getSupportedCopier(source.getClass(), target.getClass());
        //尝试copy.
        BeanCopierUtil.copyDirectly(source, target, supportedCopier);
        return target;
    }

    /**
     * 简单Copy，带逻辑.
     * @param source
     * @param targetClass
     * @param wareLogic
     * @param <T>
     * @param <S>
     * @return
     */
    public static <T,S> T simpleCopier(S source, Class<T> targetClass, Consumer<T> wareLogic){
        //生成实例.
        T target = BeanCopierUtil.simpleCopier(source, targetClass);
        //执行消费逻辑.
        if(wareLogic != null) wareLogic.accept(target);
        return target;
    }

    /**
     *
     * @param source
     * @param targetClass
     * @param preLogic
     * @param wareLogic
     * @param <T>
     * @param <S>
     * @return
     */
    public static <T,S> T simpleCopier(S source, Class<T> targetClass,Consumer<S> preLogic, Consumer<T> wareLogic){
        if(preLogic != null) preLogic.accept(source);
        //生成实例.
        T target = BeanCopierUtil.simpleCopier(source, targetClass);
        //执行消费逻辑.
        if(wareLogic != null) wareLogic.accept(target);
        return target;
    }

    /**
     * Copy属性 到给出的实例, 带后置逻辑.
     * @param source
     * @param target
     * @param <T>
     * @param <S>
     * @return
     */
    public static  <T,S> T simpleCopierDirectly(S source,T target,Consumer<T> wareLogic){
        BeanCopierUtil.simpleCopierDirectly(source, target);
        //执行消费逻辑.
        if(wareLogic != null) wareLogic.accept(target);
        return target;
    }


    /**
     * Copy属性 到给出的实例, 带后置逻辑.
     * @param source
     * @param target
     * @param <T>
     * @param <S>
     * @return
     */
    public static  <T,S> T simpleCopierDirectly(S source,T target,Consumer<S> preLogic,Consumer<T> wareLogic){
        if(preLogic != null) preLogic.accept(source);
        BeanCopierUtil.simpleCopierDirectly(source, target);
        //执行消费逻辑.
        if(wareLogic != null) wareLogic.accept(target);
        return target;
    }

    /**
     * 简单Copy，带逻辑.
     * @param source
     * @param targetClass
     * @param wareLogic
     * @param <T>
     * @param <S>
     * @return
     */
    public static <T,S> T convertCopier(S source, Class<T> targetClass,Converter converter, Consumer<T> wareLogic){

        //尝试从容器中获取支持Source和Target 的 Copier实例
        BeanCopier supportedCopier = BeanCopierUtil.getSupportedCopier(source.getClass(), targetClass);
        //尝试copy.
        T target = BeanCopierUtil.copy(source, targetClass, supportedCopier,converter);
        //执行消费逻辑.
        if(wareLogic != null) {
            wareLogic.accept(target);
        }
        return target;
    }

    /**
     * 集合Copy
     * @param sourceList
     * @param targetClass
     * @param wareLogic
     * @param <T>
     * @param <S>
     * @return
     */
    public static <T,S> List<T> listCopier(List<S> sourceList, Class<T> targetClass, Consumer<T> wareLogic){
        if(sourceList == null){
            return null;
        }
        if(sourceList.size() == 0){
            return Collections.EMPTY_LIST;
        }
        return sourceList.stream()
                .map((sourceUnit) -> BeanCopierUtil.simpleCopier(sourceUnit,targetClass,wareLogic))
                .collect(Collectors.toList());
    }


    public static <T,S> List<T> listCopier(List<S> sourceList, Class<T> targetClass, PairFunction<S,T> pairProcessorLogic){
        if(sourceList == null){
            return null;
        }
        if(sourceList.size() == 0){
            return Collections.EMPTY_LIST;
        }
        if(pairProcessorLogic == null){
            return sourceList.stream()
                    .map((sourceUnit) -> BeanCopierUtil.simpleCopier(sourceUnit,targetClass))
                    .collect(Collectors.toList());
        }else {
            return sourceList.stream()
                    .map((sourceUnit) -> pairProcessorLogic.apply(
                                sourceUnit,
                                BeanCopierUtil.simpleCopier(sourceUnit,targetClass)
                            ))
                    .collect(Collectors.toList());
        }
    }


    /**
     * 集合Copy
     * @param sourceList
     * @param targetClass
     * @param wareLogic
     * @param preLogic
     * @param <T>
     * @param <S>
     * @return
     */
    public static <T,S> List<T> listCopier(List<S> sourceList, Class<T> targetClass,Consumer<S> preLogic, Consumer<T> wareLogic){
        if(sourceList == null){
            return null;
        }
        if(sourceList.size() == 0){
            return Collections.EMPTY_LIST;
        }
        return sourceList.stream()
                .map((sourceUnit) -> BeanCopierUtil.simpleCopier(sourceUnit,targetClass,preLogic,wareLogic))
                .collect(Collectors.toList());
    }


    private static  <S,T> T copy(S source,Class<T> targetClass,BeanCopier supportedCopier){
        T target = null;
        try {
            if(targetClass.isMemberClass() && !Modifier.isStatic(targetClass.getModifiers())){
                target = BeanCopierUtil.tryInstanceDependOnOuterClass(targetClass, new ArrayList<>());
            }else {
                target = targetClass.getDeclaredConstructor().newInstance();
            }
        }catch (Exception e){
            log.error("Class : {} has't NoArgsConstructor Method for create newInstance." , targetClass);
        }
        supportedCopier.copy(source,target,null);
        return target;
    }

    private static  <S,T> T copyDirectly(S source,T target,BeanCopier supportedCopier){
        supportedCopier.copy(source,target,null);
        return target;
    }

    private static  <S,T> T copy(S source, Class<T> targetClass, BeanCopier supportedCopier, Converter converter){
        T target = null;
        try {
            if(targetClass.isMemberClass() && !Modifier.isStatic(targetClass.getModifiers())){
                target = BeanCopierUtil.tryInstanceDependOnOuterClass(targetClass, new ArrayList<>());
            }else {
                target = targetClass.getDeclaredConstructor().newInstance();
            }
        }catch (Exception e){
            log.error("Class : {} has't NoArgsConstructor Method for create newInstance." , targetClass);
        }
        supportedCopier.copy(source,target,converter);
        return target;
    }

    /**
     * 递归实例化所依赖的外部实例，创建内部类实例.
     * @param targetClass
     * @param dependInstances
     * @param <T>
     * @return
     */
    public static  <T> T tryInstanceDependOnOuterClass(Class<? super T> targetClass,List<Class<?>> dependInstances){
        dependInstances.add(targetClass);
        if(targetClass.isMemberClass() && !Modifier.isStatic(targetClass.getModifiers())){
            String innerClassName = targetClass.getName();
            Class<? super T> outerClass = null;
            try {
                outerClass = (Class<? super T>) Class.forName(innerClassName.substring(0, innerClassName.length() - (targetClass.getSimpleName().length() + 1)));
            }catch (Exception e){
                // --------
            }
            return BeanCopierUtil.tryInstanceDependOnOuterClass(outerClass,dependInstances);
        }else {
            final Object[] targetBucket = new Object[1];
            dependInstances.stream().sorted((c,n) -> -1).forEach((curr) -> {
                Constructor<?> constructor = null;
                try {
                    constructor = curr.getConstructors()[0];
                    constructor.setAccessible(true);
                    if(targetBucket[0] != null) {
                        targetBucket[0] = constructor.newInstance(targetBucket[0]);
                    }else {
                        targetBucket[0] = constructor.newInstance();
                    }
                }catch (Exception e){
                    log.info("BeanCopierUtil: instance inner class : {} privileges getting fail, please check Constructs.",
                            dependInstances.get(0));

                }
            });
            return (T)(targetBucket[0] == null ? null : targetBucket[0]);
        }
    }

    //用过类信息获取Copier实例
    private static <S,T> BeanCopier getSupportedCopier(Class<S> source,Class<T> target){
        //获取Source / Target 的类信息.
        String sourceTargetInfo = BeanCopierUtil.generatorCopierInfo(source, target);
        //尝试从容器中获取支持Source和Target 的 Copier实例
        BeanCopier supportedCopier = copierBucket.get(sourceTargetInfo);
        if(supportedCopier == null){
            supportedCopier = BeanCopier.create(source, target, false);
            copierBucket.putIfAbsent(sourceTargetInfo,supportedCopier);
        }
        return supportedCopier;
    }

    //生成类信息，存储Cache.
    private static <S,T> String generatorCopierInfo(Class<S> source,Class<T> target){
        String sourceTargetInfo = source.toString() + target.toString();
        return sourceTargetInfo;
    }


    @FunctionalInterface
    public interface PairFunction<T,R>{
        R apply(T t,R r);
    }
}

